C++은 화면 또는 키보드와 같은 입출력 장치에서 I/O 작업을 수행하기 위해서 스트림이라는 추상화를 사용한다.
스트림은 프로그램이 문자를 삽입하거나 추출할 수 있는 개체이다.
C++ 라이브러리는 표준 입력 및 출력 스트림 개체는 <iostream> 헤더를 포함한다.
표준 출력프로그램의 표준 출력은 화면에 출력하는 cout이라는 C++ 스트림을 사용해서 접근할 수 있다.
cout은 <<로 표시하는 삽입 연사자와 함께 사용된다.
cout<<"Then square root of "<<x<<"is "<<sqrt(x)<<endl;
endl 은 개행 문자(‘\n’)를 생성한다.
(endl은 \n와 달리 버퍼를 비우는 작업을 포함한다.)
모든 줄마다 버퍼를 비우면 I/O가 상당히 느려지게 된다.
삽입 연산자(<<)은 우선순위가 상대적으로 낮기 때문에 산술 연산, 비교, 논리, 비트 연산등을 수행할 수 있다.
std::cout<<"11*19= "<<11*19<<std::endl;
std::cout<<(age>65?"I'm a wise guy\n":"I am still half-baked.\n");
표준 입력C++에서 표준 입력은 오버로드한 추출 연산자 >>를 cin 스트림에 적용해서 처리한다.
std::cin은 입력 장치에서 문자를 읽고 저장할 변수 타입의 값으로 해석한다.
std::cin>>width>>height;
std::cint>>width;
std::cint>>height;
각 데이터는 공백, 탭 문자나 개행과 같은 유효한 공백 구분 기호로 구분한다.
파일 입출력C++에서는 파일에 대한 문자의 입력과 출력을 수행하기 위해서 다음과 같은 클래스를 제공한다.
<fstream>
ofstream |
파일에 쓰기 |
ifstream |
파일로부터 읽기 |
fstream |
파일 읽기 및 쓰기 |
std::cin 과 std::cout과 같은 방식으로 파일 스트림을 사용할 수 있다.
(단 파일 스트림을 실제 파일과 연관시켜야 함)
#include <fstream>
int main(void){
std::ofstream square_file;
square_file.open("squares.txt");
for(int i=0; i<10; ++i){
square_file<<i<<"^2 = "<<i*i<<std::endl;
}
square_file.close();
}
스트림의 생성자에 파일 이름을 인수로 전달해 파일을 암시적으로 열 수 있다.
(이와 같이 파일을 암시적으로 연 경우, square_file이 스코프를 벗어날 때 암시적으로 닫는다.)
#include <fstream>
int main(void){
std::ofstream square_file("squared.txt");
for(int i=0; i<10; ++i){
square_file<<i<<"^2"<<i*i<<std::endl;
}
}
.open 메소드는 디폴트로 ios::out 플래그 설정되어 있다.
ios::app |
파일이 존재하지 않으면 파일을 생성, 이어쓰기 |
ios::ate |
파일이 이미 존재하면, 프로그램은 파일의 끝으로 감 |
ios::binary |
정보가 이진수 형태로 쓰이거나 읽음 |
ios::in |
파일을 읽음. 파일이 존재하지 않으면 실패함 |
iost::out |
파일의 정보를 씀. 파일이 존재한다면 덮어씌움 |
read & write 메소드
read(char* str, int size);
write(char* str, int size);
파일 입출력 객체의 위치에서, 크기만큼 읽거나 씀
첫 번째 매개변수로 문자열의 주소값을, 두 번째 매개변수로 size를 입력
C++에서는 주로 std::cin 과 std::cout으로 파일 입출력을 처리함
seekg & seekp 메서드
seekg(숫자, flag);
seekp(숫자, flag);
seekg는 get으로 읽기, seekp는 put으로 쓰기용이다.
flag의 위치로부터 숫자 만큼 떨어짐 지점으로 엑세스한다.
flag
ios::beg |
첫 바이트 |
ios::cur |
현재 위치 |
ios::end |
끝 바이트 |
tellg & tellp 메서드현재 위치를 돌려준다.(.tellg()는 읽기 위치, .tellp()는 쓰기 위치)
get & put 메서드.get() 메서드는 한글자씩 읽어온다
(peakg은 현재값을 읽고 위치가 고정되지만, get 메서드는 현재값을 읽고 다음값을 포인팅 함)
.put(char) 메서드는 한글자씩 입력한다.
eof 메서드.eof()는 endOfFile 유무를 bool로 반환
새로운 타입을 위한 operator << 를 정의할 때, ostream에 대해서 한번만 정의하면,콘솔, 파일 및 다른 출력 스트림에서 동작한다.
stringstream표준 라이브러리에는 출력 가능한 모든 타입의 문자열을 만드는데 사용할 수 있는 stringstream을 포함한다.
sringstream의 메소드 str()은 스트림의 내부 문자열을 반환한다.
#include <iostream>
#include <fstream>
#include <sstream>
void write_something(std::ostream& os){
os <<"Hi stream, did you know that 3*3 = "<<3*3<<std::endl;
}
int main(int argc, char** argv){
std::ofstream myfile("example.txt");
std::stringstream mysstream;
write_something(std::cout);
write_something(myfile);
write_something(mysstream);
std::cout<<"mysstream is: "<<mysstream.str();
}
stringstream은 C++에서 주어진 문자열에서 필요한 자료형에 맞는 정보를 꺼내올 때 유용하다.
#include <iostream>
#include <sstream>
#include <vector>
#include <string>
using namespace std;
int main(void){
vector<long long> time;
string str="2021:12:16 20:36:15";
for(int i=0; i<str.size(); i++){
if(str[i]==':') str[i]=' ';
}
long long num=0;
stringstream stream;
stream.str(str);
while(stream>>num){
time.push_back(num);
}
long long second=0;
second+=time[0]*365*24*60*60;
second+=time[1]*30*24*60*60;
second+=time[2]*24*60*60;
second+=time[3]*60*60;
second+=time[4]*60;
second+=time[5];
cout<<second<<endl;
return 0;
}
서식 지정I/O 스트림은 헤더 파일 <iomanip>에 있는 I/O 조작기를 이용해서 서식을 지정할 수 있다.
기본적으로 C++은 부동소수점의 숫자 몇 자리만 출력함
setprecision(num)소수점 아래로 num 까지 출력
setprecision을 설정하면, 이후 모든 출력에 영향을 줌
double pi=M_PI;
cout<<"pi is "<<pi<<'\n';
cout<<"pi is ""<<setprecision(16)<<pi<<'\n';
setw(num)출력 너비를 num 만큼 설정(공백)
cout<<"pi is "<<setw(30)<<pi<<'\n';
setfill(‘-‘)<<left값을 왼쪽으로 정렬하고, 빈 공간을 선택한 문자로 채움
cout<<"pi is "<<setfill('-')<<left<<setw(30)<<pi<<'\n';
cout에 직접 플래스를 설정할 수도 있다.
자주 사용하지 않는 서식 옵션은 이러한 방법으로 설정한다.
scientific
cout.setf(ios_base::showpos);
cout<<"pi is "<<scientific<<pi<<'\n';
oct hex dec
cout<<"63 octal is "<<oct<<63<<".\n";
cout<<"63 hexadecimal is "<<hex<<63<<".\n";
cout<<"63 decimal is "<<dec<<63<<".\n";
boolalphabool 값은 기본적으로 0, 1로 출력한다.
boolalpha를 설정하면, true와 false로 표기한다.
cout<<"pi < 3 is "<<(pi<3)<<'\n';
cout<<"pi < 3 is "<<boolalpha<<(pi<3)<<'\n';
변경된 모든 서식 옵션 재설정각 옵션은 상태 변수의 비트로 표시된다.
논리적 OR 연산자로 비트 패턴을 결합해 여러 옵션을 사요할 수 있다.
int old_precision=cout.precision();
cout<<setprecision(16);
...
cout.setf(ios_base::adjustfield | ios_base::basefield | ios_base::floatfield | ios_base::showpos | ios_base::boolalpha);
cout.unsetf(ios_base::adjustfield | ios_base::basefield | ios_base::floatfield | ios_base::showpos | ios_base::boolalpha);
cout.precision(old_precision);
I/O 오류 처리스트림은 기본적으로 예외를 발생시키지 않는다.(스트림이 예외처리보다 먼저나왔기에 그동안 작성된 소프트웨어를 유지하기 위한 조치)
int main(void){
std::ifstream infile("some_missing_file.xyz");
int i;
double d;
infile>>i>>d;
std::cout<<"i is "<<i<<", d is "<<d<<'\n';
infile.close();
}
존재하지 않는 파일을 읽을 수도 있으며, 그 뒤의 프로그램을 계속 진행한다.
따라서 모든 동작을 잘 진행했는지, 확인하기 위해서 원칙적으로 각 I/O 작업 후에 오류 플래그를 확인해야 한다.
int main(void){
std::ifstream infile;
std::string filename("some_missing_file.xyz");
bool opened=false;
while(!opend){
infile.open(filename);
if(infile.good()){
opend=true;
}else{
std::cout<<"The file '"<<filename<<"' doesn't exist, give a new file name: ";
std::cin>>filename;
}
}
int i;
doub d;
infile>>i>>d;
if(infile.good()) std::cout<<"i is "<<i<<", d is "<<d<<'\n';
infile.close();
}
I/O에 대하여 예제를 사용하는 방법예외를 사용하기 위해서는 런타임 중에 각 스트림에 대해 예외를 활성화 하여야 한다.
스트림은 작업이 실패(fallbit)하거나 “잘못된” 상태(badbit)일 때마다 예외를 던진다.
cin.exceptions(ios_base::badbit | ios_base::fallbit);
cout.exceptions(ios_base::badbit | ios_base::fallbit);
std::ifstream infile("f.txt");
infile.exceptions(ios_base::badbit | ios_base::fallbit);
파일 I/O의 예외는 부분적으로 오류를 방지할 수 있다.
void with_io_exceptions(ios& io){
io.exceptions(ios_base::badbit | ios_base::fallbit);
}
int main(void){
std::ofstream outfile;
with_io_exceptions(outfile);
outfile.open("f.txt");
double o1=5.2, o2=6.2;
outfile<<o1<<o2<<std::endl;
outfile.close();
std::ifstream infile;
with_io_stream(infile);
infile.open("f.txt");
int i1, i2;
char c;
infile>>i1>>c>>i2;
std::cout<<"i1= "<<i1<<", i2= "<<i2<<'\n';
}
위의 코드에서 예외가 발생하지 않고 프로그램이 실행된다.
문법 규칙 위반은 예외가 아니다.
.3을 int로 파싱하면 0이 된다.
-5를 unsigned로 파싱하면 4294967291이 된다.(unsigned 가 32비트 long인 경우)
따라서 숫자는 공백으로 올바르게 구분하고,
작성한 타입과 동일한 타입으로 읽어야 한다.